#ifndef GUNNAR_DEFINED
#define GUNNAR_DEFINED

/*
Settings for using the output on Android systems:
#define ALLOW_USE_OF_BSP		0
#define OUTPUT_BIG_ENDIAN		1
#define OUTPUT_FLOAT_AS_FIXED	1

Settings for using the output on Windows/Mac/Linux systems:
#define ALLOW_USE_OF_BSP		1
#define OUTPUT_BIG_ENDIAN		0
#define OUTPUT_FLOAT_AS_FIXED	0
*/

#define ALLOW_USE_OF_BSP		0	// Set to 0 to make sure that the export never transparency sorts with bsp trees.
#define OUTPUT_BIG_ENDIAN		1	// Set to 0 for little endian, 1 for big endian.
#define OUTPUT_FLOAT_AS_FIXED	1	// Set to 0 for floating point, 1 for fixed point.

#include <math.h>
#include <fstream>
#include <vector>
#include <string>
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MAnimControl.h>
#include <maya/MTime.h>
#include <maya/MCommandResult.h>

#include <maya/MDagPath.h>

#include <maya/MAnimUtil.h>
#include <maya/MDataHandle.h>
#include <maya/MSelectionList.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>

#include <maya/MPoint.h>
#include <maya/MPointArray.h>
#include <maya/MFloatPointArray.h>
#include <maya/MDagPathArray.h>
#include <maya/MFloatArray.h>
#include <maya/MIntArray.h>

#include <maya/MVector.h>
#include <maya/MFloatVectorArray.h>

#include <maya/MMatrix.h>
#include <maya/MTransformationMatrix.h>

#include <maya/MQuaternion.h>

#include <maya/MBoundingBox.h>

#include <maya/MFnDagNode.h>
#include <maya/MFnMesh.h>
#include <maya/MFnIkJoint.h>
#include <maya/MFnCamera.h>
#include <maya/MFnLight.h>
#include <maya/MFnSpotLight.h>
#include <maya/MFnAnimCurve.h>
#include <maya/MFnLambertShader.h>
#include <maya/MFnReflectShader.h>
#include <maya/MFnBlinnShader.h>
#include <maya/MFnPhongShader.h>
#include <maya/MFnSet.h>
#include <maya/MFnSkinCluster.h>
#include <maya/MFnWeightGeometryFilter.h>
#include <maya/MFnGeometryFilter.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnCompoundAttribute.h>
#include <maya/MFnMatrixData.h>

#include <maya/MFnComponent.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MFnDoubleIndexedComponent.h>
#include <maya/MFnTripleIndexedComponent.h>
#include <maya/MFnComponentListData.h>

#include <maya/MItDag.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MItDependencyGraph.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MItGeometry.h>

#include <maya/MPxFileTranslator.h>
#include <maya/MPxCommand.h>
#include <maya/MArgList.h>

class Maya2Gunnar : public MPxFileTranslator
{
	/*
		Creating, constructing and interfacing methods.
	*/
public:
	Maya2Gunnar(void);
	~Maya2Gunnar(void);
	static void *creator()
	{
		return(new Maya2Gunnar);
	}
	bool haveWriteMethod() const;
	MString defaultExtension() const;
	MString filter() const;
	MStatus writer( const MFileObject& file,const MString& optionsString,FileAccessMode mode);
	MPxFileTranslator::MFileKind identifyFile(const MFileObject& file,const char* buffer,short size) const;

private:
	/*
		Internally used methods
	*/
	MStatus iterateNodes(MDagPath path);
	MStatus writeObjects(void);
	MStatus writeJoint(MDagPath &path);
	MStatus writeMesh(MDagPath &path);
	MStatus getPlug(MFnDagNode &dagNode, const MString &attr, MObject &object);
	int getParentIndex(size_t node);
	int getHierarchy(MObject node);
	int getMaterialID(MObject shader);
	bool getMaterialNode(MObject shader, MObject &ret);
	void writeMaterials(void);
	void addMaterialToList(MObject srcNode,MObject rootNode);
	bool findTextureAttribute(MFnDependencyNode &depNode,char *name,MObject &found);
	bool findAndWriteTextures(MObject node);
	void writeTexture(MFnDependencyNode &depNode,MObject tmpObj,int type);
	bool findMappingAttribute(MFnDependencyNode &depNode,MObject &found);
	void clean(void);
	void writeMatrix(const MMatrix &matrix);
	bool supportedMaterialType(MString tname);
	bool isNodeStored(MDagPath path);

	MStatus writeNodes(void);
	bool writeDecomposed(MMatrix **matrixes,int num);

	void writeVertices(int mat,const MFloatPointArray &vertices,const MIntArray &faces,const MIntArray &mindices);
	void writeTVertices(int mat,const MFloatArray &upoint,const MFloatArray &vpoint,const MIntArray &faces,const MIntArray &mindices);
	void writeNVertices(int mat,const MFloatVectorArray &vertices,const MIntArray &faces,const MIntArray &mindices);

	template<typename valType>
	const MStatus getPlugVal(const MFnDependencyNode &node,const MString &name,valType &ret) const
	{
		MStatus status;
		MPlug plug=node.findPlug(name,&status);
		if(status==MS::kSuccess)
		{
			status=plug.getValue(ret);
		}
		return(status);
	}

private:
	/*
		Internally used classes
	*/
	class iBitmapSetting
	{
	public:
		iBitmapSetting() {;}
		~iBitmapSetting() {;}
		float uoffset,voffset;
		float utile,vtile;
		float angle,strength;
		/*
			No filtering	0x00000001
			Clamp U			0x00000002
			Clamp V			0x00000004
			Filter rgbalpha	0x00000008
		*/
		int filter;
	}; // class iBitmapSetting

	class iMaterial
	{
	public:
		iMaterial() {;}
		~iMaterial() {;}
		MString name;
		MObject node;
		float diffuse[3];
		float specular[3];
		float ambient[3];
		float illumination[3];
		float shininess;
		float transparency;
		bool gottexture;
		bool sortalpha;
		iBitmapSetting textureparams;
	}; // class iMaterial

	class iInstance
	{
	public:
		iInstance(const MString &n,const MDagPath &p) : name(n),path(p),writeed(false) {;}
		~iInstance(void) {;}
		MString name;
		MDagPath path;
		bool writeed;
	}; // class iInstance

	class iWeight
	{
	public:
		iWeight(unsigned char i, float w) : index(i), weight(w) {;}
		~iWeight(void) {;}

		unsigned char index;
		float weight;

		bool operator==(const iWeight &other) const
		{
			return(other.index==index&&other.weight==weight);
		}
		bool operator!=(const iWeight &other) const
		{
			return(other.index!=index||other.weight!=weight);
		}
	}; // class iWeight

	class iVertexSet
	{
	public:
		iVertexSet(int i, int n, int t, int w) : idx(i),nidx(n),tidx(t),widx(w) {;}
		~iVertexSet(void) {;}

		int idx,nidx,tidx,widx;

		bool operator==(const iVertexSet &other) const
		{
			return(other.idx==idx&&other.nidx==nidx&&other.tidx==tidx&&other.widx==widx);
		}
	}; // class iVertexSet

private:
	/*
		Internally used variables
	*/
	int m_numofobjects;
	int m_cmaterial;
	std::vector<MDagPath> m_nodes;
	std::vector<size_t> m_jointIndex;
	std::vector<iMaterial> m_materials;
	std::vector<iInstance> m_iInstances;
	MMatrix **m_matrixes;
public:
	/*
		file output variables and methods
	*/
	std::ofstream *m_output;
	template<typename valType>
	void push(valType &v)
	{
		if(OUTPUT_BIG_ENDIAN==0)
		{
			for(int t=0;t<sizeof(valType);t++)
			{
				m_output->put(((unsigned char*)&v)[t]);
			}
		}
		else
		{
			for(int t=sizeof(valType);--t>=0;)
			{
				m_output->put(((unsigned char*)&v)[t]);
			}
		}
	}

	void push(float v)
	{
		int tv=*((int*)&v);
		if(OUTPUT_FLOAT_AS_FIXED==1)
		{
			tv=(int)(v*0x10000);
		}
		if(OUTPUT_BIG_ENDIAN==0)
		{
			for(int t=0;t<sizeof(float);t++)
			{
				m_output->put(((unsigned char*)&tv)[t]);
			}
		}
		else
		{
			for(int t=sizeof(float);--t>=0;)
			{
				m_output->put(((unsigned char*)&tv)[t]);
			}
		}
	}

	void push_string(const char* str)
	{
		int t=0;
		unsigned char c=1;
		while(c)
		{
			c=(unsigned char)str[t++];
			m_output->put(c);
		}
	}

}; // class Maya2Gunnar

#define bsp_epsilon 0.00001f

class BSPTree
{
public:
	BSPTree(MFloatPointArray &vertices,MFloatVectorArray &normals,MFloatArray &upoints, MFloatArray &vpoints,MIntArray &indices,int foff,int num_indices, bool textured);
	~BSPTree(void);
	void generate(void);
	void extractVertices(MFloatPointArray &vertices,MFloatVectorArray &normals,MFloatArray &upoints, MFloatArray &vpoints,int &numfaces);
	void pushTriangles(Maya2Gunnar *plugin);

private:

	class vertex
	{
	public:
		MFloatVector v,n;
		float tx,ty;

		bool operator== (const vertex &vt) const;
	}; // class vertex

	class triangle
	{
	public:
		unsigned short i1,i2,i3;
		MFloatVector dir;
		float p;
		unsigned short front,back;
	}; // class triangle

	std::vector<vertex> m_vertices;
	std::vector<triangle> m_triangles;
	bool m_textured;

	unsigned short recursion(unsigned short start, unsigned short &end);
	vertex cut(int a,int b,float fa,float fb,float p);
	inline float tobsp_epsilon(float a) const
	{
		if(abs(a)<=bsp_epsilon) {return(0);}
		return(a);
	}
	unsigned short findVertex(const vertex &v);

}; // class BSPTree


/*
	File format:

	unsigned int magic[2];	// magic number for file identification: 0xb1d4abad,0x2b17fac
	int materialnum;
	<material>[materialnum]
		string name;
		float diffuse[3];
		float specular[3];
		float ambient[3];
		float illumination[3];
		float shininess;
		float transparency;
		
		int bitmapmask;	// bitset availible bitmaps
		<bitmap>[count_bits(bitmapmask)]
			string filename;	// zero ended
			float strength;
			int filter;
		</bitmap>
	</material>

	int numofobjects;
	<object>[numofobjects]
		string name;
		unsigned char objecttype;
		<mesh>{objecttype==0}
			int flags;
			float boundbox[6];	// min(x,y,z),max(x,y,z)
			int numofsubmeshes;
			<submesh>[numofsubmeshes]
				unsigned short material;	// reference to <material>
				float center[3];
				int numtriangles;
				int numvertices;
				int skinweights;
				unsigned char meshtype;
				<unsorted>{meshtype==0}
					unsigned short faces[numtriangles*3];
				</unsorted>
				<bsp>{meshtype==1}	// Will never be used if skinweights!=0
					<faces>[numtriangles]
						unsigned short i1,i2,i3;
						float plane[4]; // dir+p
						unsigned short front,back;
					</faces>
				</bsp>
				float vertices[numvertices*(<material>[material].bitmapmask==0?6:8)]; // texture (if existing),normal,pos
				<weights>[vertices]
					<weight>[skinweights]
						float influense;
					</weight>
					<jointindexes>[skinweights]
						unsigned char index;
					</jointindexes>
				</weights>
			</submesh>
		</mesh>
		<joint>{objecttype==1}
			float bind[4][4];
			float georef[4][4];
		</joint>
	</object>

	int numofnodes;
	int fps;
	int frames;
	<node>[numofnodes]
		string name;
		int parentnode;	// -1==root. This value is always less than the current node index.
		int objectref;	// reference to <object> -1 if no reference
		<transform>
			<channel>[10]
				unsigned char type;
				<static_channel>{type==0}
					float static;
				</static_channel>
				<raw_channel>{type==1}
					float raw[frames];
				</raw_channel>
			</channel>
		</transform animation>
	</node>

	Remarks to nodes:
	They are always sorted in a rising order, so they can be drawn and calculated in a linear list.
*/

#endif // GUNNAR_DEFINED
